package oa.hleast.xswl.service.oms.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ijpay.core.kit.PayKit;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import oa.hleast.common.web.exception.BizException;
import oa.hleast.common.web.util.RequestUtils;
import oa.hleast.xswl.common.constant.CommonStatusEnum;
import oa.hleast.xswl.common.enums.OrderStatusEnum;
import oa.hleast.xswl.common.enums.PayTypeEnum;
import oa.hleast.xswl.common.enums.RefundAndAfterSaleEnum;
import oa.hleast.xswl.mapper.oms.OmsOrderRefundMapper;
import oa.hleast.xswl.mapper.oms.OrderItemMapper;
import oa.hleast.xswl.mapper.oms.OrderMapper;
import oa.hleast.xswl.pojo.domain.OmsOrderRefund;
import oa.hleast.xswl.pojo.domain.oms.OmsOrder;
import oa.hleast.xswl.pojo.domain.oms.OmsOrderItem;
import oa.hleast.xswl.pojo.domain.oms.OmsOrderRefundLog;
import oa.hleast.xswl.pojo.dto.SysUserDto;
import oa.hleast.xswl.pojo.form.OmsOrderRefundForm;
import oa.hleast.xswl.pojo.vo.oms.OmsOrderRefundVo;
import oa.hleast.xswl.pojo.vo.oms.ShowOmsOrderRefundVo;
import oa.hleast.xswl.pojo.vo.oms.ShowOrderRefundLogVo;
import oa.hleast.xswl.service.ISysUserService;
import oa.hleast.xswl.service.oms.OmsOrderRefundLogService;
import oa.hleast.xswl.service.oms.OmsOrderRefundService;
import oa.hleast.xswl.service.pay.WeiXinPayClientService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

/**
 * @author: 302
 * @date: 2021-10-30 17:42:11
 * @description: 订单退款表(OmsOrderRefund)接口实现
 */
@Transactional
@AllArgsConstructor
@Slf4j
@Service("omsOrderRefundService")
public class OmsOrderRefundServiceImpl extends ServiceImpl<OmsOrderRefundMapper, OmsOrderRefund> implements OmsOrderRefundService {

    private final OrderMapper orderMapper;
    private final OrderItemMapper orderItemMapper;
    private final ISysUserService sysUserService;
    private final WeiXinPayClientService weiXinPayClientService;
    private final OmsOrderRefundLogService orderRefundLogService;

    /**
     * 申请退款
     */
    @Override
    public Boolean requestRefund(OmsOrderRefundForm form) {
        Long userId = RequestUtils.getUserId();
        Long orderId = form.getOrderId();
        OmsOrder omsOrder = orderMapper.selectById(orderId);
        OmsOrderItem orderItem = orderItemMapper.selectById(form.getOrderItemId());
        Integer code = form.getCode();

        // TODO martin 校验前置状态
        // TODO martin 校验金额

        // 保存退款申请
        OmsOrderRefund pojo = new OmsOrderRefund();
        BeanUtil.copyProperties(form, pojo);
        pojo.setUserId(userId);
        pojo.setRefundType(code);
        pojo.setOrderBeforeStatus(omsOrder.getStatus());
        pojo.setStoreId(omsOrder.getStoreId());
        pojo.setSpuId(orderItem.getSpuId());

        boolean save = this.save(pojo);

        // 修改订单售后状态
        omsOrder.setRefund(true);
        int updateOrder = orderMapper.updateById(omsOrder);
        orderItem.setSubRefund(true);
        int updateItem = orderItemMapper.updateById(orderItem);

        // 操作记录
        String msg = String.format("买家发起了%s申请", Objects.requireNonNull(RefundAndAfterSaleEnum.getValue(code)).getName());
        OmsOrderRefundLog refundLog = OmsOrderRefundLog.builder()
                .refundId(pojo.getId())
                .orderId(orderId)
                .orderItemId(orderItem.getId())
                .operationUserId(userId)
                .msg(msg)
                .build();

        orderRefundLogService.saveRefundLog(refundLog);

        // TODO 信息通知商家

        return save && updateOrder > 0 && updateItem > 0;
    }

    @Override
    public OmsOrderRefundVo getDetail(Long spuId) {
        OmsOrderRefund omsOrderRefund = this.list(new LambdaQueryWrapper<OmsOrderRefund>()
                .eq(OmsOrderRefund::getSpuId, spuId)
                .last("limit 1")).get(0);

        OmsOrderRefundVo vo = new OmsOrderRefundVo();
        BeanUtil.copyProperties(omsOrderRefund, vo);
        return vo;
    }

    @Override
    public OmsOrderRefundVo getDetail(Long orderId, Long orderItemId) {
        log.info("orderId ={},orderItemId = {}", orderId, orderItemId);

        OmsOrder omsOrder = orderMapper.selectById(orderId);
        OmsOrderItem orderItem = orderItemMapper.selectById(orderItemId);

        List<OmsOrderRefund> omsOrderRefundList = this.list(new LambdaQueryWrapper<OmsOrderRefund>()
                .eq(OmsOrderRefund::getOrderId, orderId)
                .eq(OmsOrderRefund::getOrderItemId, orderItemId)
                .last("limit 1"));

        if (CollectionUtils.isNotEmpty(omsOrderRefundList)) {
            OmsOrderRefund orderRefund = omsOrderRefundList.get(0);
            OmsOrderRefundVo vo = new OmsOrderRefundVo();
            BeanUtil.copyProperties(orderRefund, vo);
            // 是否显示商家确认退款订单按钮
            vo.setDisplayConfirm(getWhetherConfirmReceipt(omsOrder, orderItem, orderRefund));
            return vo;
        }


        return null;
    }

    /**
     * 重新申请退款/退款退货
     */
    @Override
    public Boolean againRequestRefund(OmsOrderRefundForm form) {
        OmsOrderRefund orderRefund = this.getById(form.getId());
        OmsOrder omsOrder = orderMapper.selectById(orderRefund.getOrderId());
        OmsOrderItem orderItem = orderItemMapper.selectById(orderRefund.getOrderItemId());

        // 校验是否是自己的申请
        Long userId = RequestUtils.getUserId();
        if (!orderRefund.getUserId().equals(userId)) {
            throw new BizException("您只能重新申请自己的退款订单");
        }

        // 校验是否重新申请
        if (!getAgainApply(omsOrder, orderItem, orderRefund)) {
            throw new BizException("权限不足");
        }

        orderRefund.setRefundType(form.getCode());
        orderRefund.setReason(form.getReason());
        orderRefund.setPrice(form.getPrice());
        orderRefund.setDescription(form.getDescription());
        orderRefund.setEvidence(form.getEvidence());
        orderRefund.setAgreeRefund(0);
        orderRefund.setApplyNum(orderRefund.getApplyNum() + 1);
        orderRefund.setApplyDisplay(true);
        boolean update = this.updateById(orderRefund);

        // 操作记录
        String msg = String.format("买家第%d次发起了%s申请", orderRefund.getApplyNum(), Objects.requireNonNull(RefundAndAfterSaleEnum.getValue(orderRefund.getRefundType())).getName());
        OmsOrderRefundLog refundLog = OmsOrderRefundLog.builder()
                .refundId(orderRefund.getId())
                .orderId(omsOrder.getId())
                .orderItemId(orderItem.getId())
                .operationUserId(userId)
                .msg(msg)
                .build();

        orderRefundLogService.saveRefundLog(refundLog);

        return update;
    }

    /**
     * 取消退款/退款退货申请
     */
    @Override
    public Boolean cancelRefund(Long orderId, Long orderItemId) {
        log.info("orderId = {} ,orderItemId = {}", orderId, orderItemId);

        OmsOrderRefund orderRefund = this.list(new LambdaQueryWrapper<OmsOrderRefund>()
                .eq(OmsOrderRefund::getOrderId, orderId)
                .eq(OmsOrderRefund::getOrderItemId, orderItemId)
                .last("limit 1")).get(0);

        OmsOrder omsOrder = orderMapper.selectById(orderRefund.getOrderId());
        OmsOrderItem orderItem = orderItemMapper.selectById(orderItemId);

        // 校验是否是自己的申请
        Long userId = RequestUtils.getUserId();
        if (!orderRefund.getUserId().equals(userId)) {
            throw new BizException("您只能重新申请自己的退款订单");
        }

        // 校验是否能取消退款/退款退货申请
        if (!getCancelRefund(omsOrder, orderItem, orderRefund)) {
            throw new BizException("权限不足");
        }

        boolean update = this.update(new LambdaUpdateWrapper<OmsOrderRefund>()
                .set(OmsOrderRefund::getApplyDisplay, false)
                .eq(OmsOrderRefund::getId, orderRefund.getId())
        );

        // 操作记录
        String msg = String.format("买家取消了%s申请", Objects.requireNonNull(RefundAndAfterSaleEnum.getValue(orderRefund.getRefundType())).getName());
        OmsOrderRefundLog refundLog = OmsOrderRefundLog.builder()
                .refundId(orderRefund.getId())
                .orderId(omsOrder.getId())
                .orderItemId(orderItem.getId())
                .operationUserId(userId)
                .msg(msg)
                .build();

        orderRefundLogService.saveRefundLog(refundLog);

        return update;
    }

    /**
     * 商家同意退款/退款退货
     */
    @Override
    public Boolean storeAgreeRefund(Long refundId, Integer agreeRefund, String remark) {
        // 校验是否是商家自己的订单
        OmsOrderRefund omsOrderRefund = this.getById(refundId);
        Long userId = RequestUtils.getUserId();
        SysUserDto sysUserDto = sysUserService.getSysUserDtoById(userId);
        OmsOrder omsOrder = orderMapper.selectById(omsOrderRefund.getOrderId());
        OmsOrderItem orderItem = orderItemMapper.selectById(omsOrderRefund.getOrderItemId());
        Long storeId = sysUserDto.getStoreId();
        Long orderStoreId = omsOrder.getStoreId();

        if (!storeId.equals((long) orderStoreId)) {
            throw new BizException("您只能管理自己的订单");
        }

        omsOrderRefund.setAgreeRefund(agreeRefund);
        omsOrderRefund.setStoreRemark(remark);

        // 如果是仅退款则直接退款
        if (1 == omsOrderRefund.getRefundType() && agreeRefund.equals((int) CommonStatusEnum.PASS.getCode())) {
            // 退款
            String outRefundNo = PayKit.generateStr();
            Boolean isRefundSuccess = orderRefund(omsOrder, orderItem, outRefundNo, "商家退款");
            if (isRefundSuccess) {
                omsOrderRefund.setRefundNo(outRefundNo);
                omsOrderRefund.setThirdRefundStatus(10);
            } else {
                omsOrderRefund.setRefundNo(outRefundNo);
                omsOrderRefund.setThirdRefundStatus(11);
            }
            omsOrderRefund.setRefundTarget(omsOrder.getOrderType());
            // 修改订单状态
            omsOrder.setStatus(OrderStatusEnum.SHUTDOWN.getCode());
        }


        // 修改订单售后状态
        omsOrder.setRefund(true);
        orderMapper.updateById(omsOrder);
        orderItem.setSubRefund(true);
        orderItemMapper.updateById(orderItem);

        boolean update = this.updateById(omsOrderRefund);

        // 操作记录
        String msg = "";
        if (agreeRefund.equals((int) CommonStatusEnum.PASS.getCode())) {
            msg = String.format("商家同意了%s申请", Objects.requireNonNull(RefundAndAfterSaleEnum.getValue(omsOrderRefund.getRefundType())).getName());
        } else if (agreeRefund.equals((int) CommonStatusEnum.NO_PASS.getCode())) {
            msg = String.format("商家拒绝了%s申请,原因: %s", Objects.requireNonNull(RefundAndAfterSaleEnum.getValue(omsOrderRefund.getRefundType())).getName(), remark);
        }


        OmsOrderRefundLog refundLog = OmsOrderRefundLog.builder()
                .refundId(refundId)
                .orderId(omsOrder.getId())
                .orderItemId(orderItem.getId())
                .operationUserId(userId)
                .msg(msg)
                .build();
        orderRefundLogService.saveRefundLog(refundLog);

        if (1 == omsOrderRefund.getRefundType() && agreeRefund.equals((int) CommonStatusEnum.PASS.getCode())) {
            String shutdownMsg = String.format("%s订单完成", Objects.requireNonNull(RefundAndAfterSaleEnum.getValue(omsOrderRefund.getRefundType())).getName());
            ;
            OmsOrderRefundLog shutdownLog = OmsOrderRefundLog.builder()
                    .refundId(refundId)
                    .orderId(omsOrder.getId())
                    .orderItemId(orderItem.getId())
                    .operationUserId(userId)
                    .msg(shutdownMsg)
                    .build();
            orderRefundLogService.saveRefundLog(shutdownLog);
        }

        return update;
    }

    /**
     * 商家确认收货
     */
    @Override
    public Boolean storeConfirmReceipt(Long refundId) {
        // 检查是否是自己的订单
        OmsOrderRefund omsOrderRefund = this.getById(refundId);
        Long userId = RequestUtils.getUserId();
        SysUserDto sysUserDto = sysUserService.getSysUserDtoById(userId);
        OmsOrder omsOrder = orderMapper.selectById(omsOrderRefund.getOrderId());
        OmsOrderItem orderItem = orderItemMapper.selectById(omsOrderRefund.getOrderItemId());
        Long storeId = sysUserDto.getStoreId();
        Long orderStoreId = omsOrder.getStoreId();
        if (!storeId.equals((long) orderStoreId)) {
            throw new BizException("您只能管理自己的订单");
        }

        // 检查能否确认收货
        if (!getWhetherConfirmReceipt(omsOrder, orderItem, omsOrderRefund)) {
            throw new BizException("权限不足");
        }

        omsOrderRefund.setConfirm(1);
        boolean update = this.updateById(omsOrderRefund);

        // 退款
        String outRefundNo = PayKit.generateStr();
        Boolean isRefundSuccess = orderRefund(omsOrder, orderItem, outRefundNo, "商家退款");
        if (isRefundSuccess) {
            omsOrderRefund.setRefundNo(outRefundNo);
            omsOrderRefund.setThirdRefundStatus(10);
        } else {
            omsOrderRefund.setRefundNo(outRefundNo);
            omsOrderRefund.setThirdRefundStatus(11);
        }
        omsOrderRefund.setRefundTarget(omsOrder.getOrderType());

        // 修改订单状态
        // 修改订单售后状态
        omsOrder.setRefund(true);
        omsOrder.setStatus(OrderStatusEnum.SHUTDOWN.getCode());
        orderMapper.updateById(omsOrder);
        orderItem.setSubRefund(true);
        orderItemMapper.updateById(orderItem);

        // 操作记录
        String msg = "商家确认收货";
        OmsOrderRefundLog refundLog = OmsOrderRefundLog.builder()
                .refundId(refundId)
                .orderId(omsOrder.getId())
                .orderItemId(orderItem.getId())
                .operationUserId(userId)
                .msg(msg)
                .build();
        orderRefundLogService.saveRefundLog(refundLog);

        String shutdownMsg = String.format("%s订单完成", Objects.requireNonNull(RefundAndAfterSaleEnum.getValue(omsOrderRefund.getRefundType())).getName());
        ;
        OmsOrderRefundLog shutdownLog = OmsOrderRefundLog.builder()
                .refundId(refundId)
                .orderId(omsOrder.getId())
                .orderItemId(orderItem.getId())
                .operationUserId(userId)
                .msg(shutdownMsg)
                .build();
        orderRefundLogService.saveRefundLog(shutdownLog);

        return update;
    }

    /**
     * 买家填写退货信息
     *
     * @param deliveryCompany 物流公司
     * @param deliverySn      物流单号
     */
    @Override
    public Boolean saveDeliveryInfo(Long refundId, String deliveryCompany, String deliverySn) {

        OmsOrderRefund omsOrderRefund = this.getById(refundId);
        log.info("refundId = {} ,omsOrderRefund = {}", refundId, omsOrderRefund);
        // 检查商家是否已同意退款
        Integer agreeRefund = omsOrderRefund.getAgreeRefund();
        if (0 == agreeRefund) {
            throw new BizException("商家还未同意退款");
        } else if (11 == agreeRefund) {
            throw new BizException("商家拒绝退款");
        }

        // 校验单号是否正确 TODO Martin

        omsOrderRefund.setDeliveryCompany(deliveryCompany);
        omsOrderRefund.setDeliverySn(deliverySn);
        omsOrderRefund.setWhetherDelivery(true);
        boolean update = this.updateById(omsOrderRefund);

        // 操作记录
        String msg = String.format("买家填写了物流信息,物流单号 %s", deliverySn);
        OmsOrderRefundLog refundLog = OmsOrderRefundLog.builder()
                .refundId(refundId)
                .orderId(omsOrderRefund.getOrderId())
                .orderItemId(omsOrderRefund.getOrderItemId())
                .operationUserId(RequestUtils.getUserId())
                .msg(msg)
                .build();
        orderRefundLogService.saveRefundLog(refundLog);

        return update;
    }

    /**
     * 退款详情
     *
     * @param orderId     订单ID
     * @param orderItemId 订单子项ID
     * @return 退款详情
     */
    @Override
    public ShowOmsOrderRefundVo getRefundDetails(Long orderId, Long orderItemId) {
        OmsOrder omsOrder = orderMapper.selectById(orderId);
        OmsOrderItem orderItem = orderItemMapper.selectById(orderItemId);

        OmsOrderRefund omsOrderRefund = this.list(new LambdaQueryWrapper<OmsOrderRefund>()
                .eq(OmsOrderRefund::getOrderId, orderId)
                .eq(OmsOrderRefund::getOrderItemId, orderItemId)
                .last("limit 1")).get(0);

        ShowOmsOrderRefundVo vo = new ShowOmsOrderRefundVo();
        BeanUtil.copyProperties(omsOrderRefund, vo);

        List<ShowOrderRefundLogVo> list = orderRefundLogService.findRefundLog(omsOrderRefund.getId());
        vo.setSchedule(list);

        // 是否能取消退款
        Boolean cancelRefund = getCancelRefund(omsOrder, orderItem, omsOrderRefund);
        // 是否能填写物流
        Boolean writeLogistics = getWriteLogistics(omsOrder, orderItem, omsOrderRefund);
        // 是否能重新申请
        Boolean againApply = getAgainApply(omsOrder, orderItem, omsOrderRefund);
        vo.setCancelRefund(cancelRefund);
        vo.setWriteLogistics(writeLogistics);
        vo.setAgainApply(againApply);

        return vo;
    }

    /**
     * 是否能填写物流信息
     * 条件1:退款申请类型为退货退款
     * 条件2:商家同意退货退款
     * 条件3:买家未填写过物流信息
     */
    private Boolean getWriteLogistics(OmsOrder omsOrder, OmsOrderItem orderItem, OmsOrderRefund omsOrderRefund) {
        Integer refundType = omsOrderRefund.getRefundType();
        Integer agreeRefund = omsOrderRefund.getAgreeRefund();
        Boolean whetherDelivery = omsOrderRefund.getWhetherDelivery();

        return refundType.equals(2) && agreeRefund.equals(10) && !whetherDelivery;
    }

    /**
     * 是否能取消退款申请
     * 条件1:订单已付款后 至 订单关闭前
     * 条件2:申请过退款申请
     * 条件3:商家同意退款前 或 拒绝退款
     * 条件4:退款申请处于生效状态
     */
    private Boolean getCancelRefund(OmsOrder omsOrder, OmsOrderItem orderItem, OmsOrderRefund omsOrderRefund) {
        Integer status = omsOrder.getStatus();
        Boolean refund = omsOrder.getRefund();
        Integer agreeRefund = omsOrderRefund.getAgreeRefund();
        Boolean applyDisplay = omsOrderRefund.getApplyDisplay();
        return status > OrderStatusEnum.PAID.getCode() && status < OrderStatusEnum.SHUTDOWN.getCode() && refund && applyDisplay && (agreeRefund.equals(0) || agreeRefund.equals(11));
    }

    /**
     * 是否能重新申请
     * 条件1:订单已付款后 至 订单关闭前
     * 条件2:退款申请未生效状态 或 商家拒绝退款
     */
    private Boolean getAgainApply(OmsOrder omsOrder, OmsOrderItem orderItem, OmsOrderRefund omsOrderRefund) {
        Integer status = omsOrder.getStatus();
        Boolean applyDisplay = omsOrderRefund.getApplyDisplay();
        Integer agreeRefund = omsOrderRefund.getAgreeRefund();

        return status > OrderStatusEnum.PAID.getCode() && status < OrderStatusEnum.SHUTDOWN.getCode() && (!applyDisplay || agreeRefund.equals(11));
    }

    /**
     * 是否展示确认退款物流按钮
     * 条件0:已支付后 ~ 订单关闭前
     * 条件1:申请过退款
     * 条件2:退款类型为 退货退款
     * 条件3:商家同意退货退款
     * 条件4:买家填写了退货的物流信息
     * 条件5:货款还未会还到原资金账号
     */
    private Boolean getWhetherConfirmReceipt(OmsOrder omsOrder, OmsOrderItem omsOrderItem, OmsOrderRefund omsOrderRefund) {
        Integer status = omsOrder.getStatus();
        Boolean isRefund = omsOrder.getRefund();
        Integer refundType = omsOrderRefund.getRefundType();
        Integer agreeRefund = omsOrderRefund.getAgreeRefund();
        Boolean applyDisplay = omsOrderRefund.getApplyDisplay();
        Integer thirdRefundStatus = omsOrderRefund.getThirdRefundStatus();
        return status > OrderStatusEnum.PAID.getCode() && status < OrderStatusEnum.SHUTDOWN.getCode()
                && isRefund && refundType.equals(2) && agreeRefund.equals(10) && applyDisplay
                && (thirdRefundStatus.equals(0) || thirdRefundStatus.equals(11));

    }

    /**
     * 各渠道退款
     *
     * @param omsOrder     订单项
     * @param omsOrderItem 订单子项
     * @param refundNo     退款单号
     * @param reason       退款备注
     * @return 是否成功退款
     */
    private Boolean orderRefund(OmsOrder omsOrder, OmsOrderItem omsOrderItem, String refundNo, String reason) {
        String orderSn = omsOrder.getOrderSn();
        Integer payAmount = omsOrder.getPayAmount().intValue();
        Integer payType = omsOrder.getPayType();
        switch (Objects.requireNonNull(PayTypeEnum.getByCode(payType))) {
            case WEIXIN:
                return weiXinPayClientService.refund(null, orderSn, refundNo, payAmount, payAmount, reason);
            case ALIPAY:
                break;
            case POINT:
                break;
            default:
                throw new BizException("系统暂不支持该支付方式~");
        }
        throw new BizException("退款异常");
    }
}
